﻿using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;
using System.Windows.Media;
using System.Windows.Media.Animation;

using Microsoft.Phone.Controls;

using WP7Contrib.View.Controls.Extensions;

namespace WP7Contrib.View.Controls.Behaviors
{
    /// <summary>
    /// This behaviour, when associated with an element within a Pivot control, causes any child elements 
    /// with an AnimationLevel set to slide into view.
    /// </summary>
    public class PivotSlideBehaviour : Behavior<FrameworkElement>
    {
        #region fields

        private Pivot _pivot;

        private bool _selectionChanged = false;

        private int _pivotIndex;

        #endregion

        #region DPs
        
        /// <summary>
        /// Gets the 'animation level' for the given element. The level determines
        /// by how much the sliding of this element is delayed. This is an attached property.
        /// </summary>
        public static int GetAnimationLevel(DependencyObject obj)
        {
            return (int)obj.GetValue(AnimationLevelProperty);
        }

        /// <summary>
        /// Sets the 'animation level' for the given element. The level determines
        /// by how much the sliding of this element is delayed. This is an attached property.
        /// </summary>
        public static void SetAnimationLevel(DependencyObject obj, int value)
        {
            obj.SetValue(AnimationLevelProperty, value);
        }

        /// <summary>
        /// Identifies the AnimationLevel dependency property. 
        /// </summary>
        public static readonly DependencyProperty AnimationLevelProperty =
            DependencyProperty.RegisterAttached("AnimationLevel", typeof(int),
            typeof(PivotSlideBehaviour), new PropertyMetadata(-1));
        
        #endregion

        #region Behaviour overrides

        protected override void OnAttached()
        {
            base.OnAttached();

            AssociatedObject.Loaded += AssociatedObject_Loaded;
        }
                
        protected override void OnDetaching()
        {
            base.OnDetaching();

            AssociatedObject.Loaded -= AssociatedObject_Loaded;

            if (_pivot != null)
            {
                _pivot.SelectionChanged -= Pivot_SelectionChanged;
                _pivot.ManipulationCompleted -= Pivot_ManipulationCompleted;
            }
        }

        #endregion 

        #region private

        private void AssociatedObject_Loaded(object sender, System.Windows.RoutedEventArgs e)
        {
            // locate the pivot control that this list is within
            _pivot = AssociatedObject.Ancestors<Pivot>().Single() as Pivot;

            // and its index within the pivot
            _pivotIndex = _pivot.Items.IndexOf(AssociatedObject.Ancestors<PivotItem>().Single());


            _pivot.SelectionChanged += Pivot_SelectionChanged;

            // handle manipulation events which occur when the user
            // moves between pivot items
            _pivot.ManipulationCompleted += Pivot_ManipulationCompleted;
        }

        private void Pivot_ManipulationCompleted(object sender, System.Windows.Input.ManipulationCompletedEventArgs e)
        {

            if (!_selectionChanged)
                return;

            _selectionChanged = false;

            if (_pivotIndex != _pivot.SelectedIndex)
                return;

            // determine which direction this tab will be scrolling in from
            bool fromRight = e.TotalManipulation.Translation.X <= 0;

            var list = AssociatedObject as ItemsControl;
            if (list != null)
            {
                // iterate over each of the items in view
                var items = list.GetItemsInView().ToList();
                for (int index = 0; index < items.Count; index++)
                {
                    var lbi = items[index];

                    AssociatedObject.Dispatcher.BeginInvoke(() =>
                    {
                        var animationTargets = lbi.Descendants()
                                                  .Where(p => GetAnimationLevel(p) > -1);
                        foreach (FrameworkElement target in animationTargets)
                        {
                            // trigger the required animation
                            GetSlideAnimation(target, fromRight).Begin();
                        }
                    });
                };
            }
            else
            {
                AssociatedObject.Dispatcher.BeginInvoke(() =>
                {
                    var animationTargets = AssociatedObject.Descendants()
                                                           .Where(p => GetAnimationLevel(p) > -1);
                    foreach (FrameworkElement target in animationTargets)
                    {
                        // trigger the required animation
                        GetSlideAnimation(target, fromRight).Begin();
                    }
                });
            }
        }

        private void Pivot_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            _selectionChanged = true;
        }


        /// <summary>
        /// Creates a TranslateTransform and associates it with the given element, returning
        /// a Storyboard which will animate the TranslateTransform with a SineEase function
        /// </summary>
        private static Storyboard GetSlideAnimation(FrameworkElement element, bool fromRight)
        {
            double from = fromRight ? 80 : -80;

            Storyboard sb;
            double delay = (PivotSlideBehaviour.GetAnimationLevel(element)) * 0.1 + 0.1;

            TranslateTransform trans = new TranslateTransform() { X = from };
            element.RenderTransform = trans;

            sb = new Storyboard();
            sb.BeginTime = TimeSpan.FromSeconds(delay);
            sb.Children.Add(TiltBehaviour.CreateAnimation(from, 0, 0.8, "X", trans));
            return sb;
        }

        #endregion
    }
}
